package com.cwx.mychat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cwx.mychat.entity.constant.CodeConstant;
import com.cwx.mychat.entity.constant.FileSettingConstant;
import com.cwx.mychat.entity.constant.SysSettingConstant;
import com.cwx.mychat.entity.dto.TokenUserInfoDto;
import com.cwx.mychat.entity.enums.*;
import com.cwx.mychat.entity.po.ChatMessage;
import com.cwx.mychat.entity.po.ChatSession;
import com.cwx.mychat.entity.po.UserContact;
import com.cwx.mychat.entity.vo.MessageSendVo;
import com.cwx.mychat.exception.BusinessException;
import com.cwx.mychat.mappers.ChatMessageMapper;
import com.cwx.mychat.mappers.ChatSessionMapper;
import com.cwx.mychat.mappers.UserContactMapper;
import com.cwx.mychat.openai.service.AIChatAssistantService;
import com.cwx.mychat.openai.service.AIChatSendMessage;
import com.cwx.mychat.openai.service.AIChatService;
import com.cwx.mychat.service.ChatMessageService;
import com.cwx.mychat.service.ChatSessionService;
import com.cwx.mychat.utils.R;
import com.cwx.mychat.utils.cipher.AESUtils;
import com.cwx.mychat.utils.common.CommonUtil;
import com.cwx.mychat.utils.minio.MinioUtil;
import com.cwx.mychat.utils.redis.RedisComponent;
import com.cwx.mychat.utils.stringUtils.StringUtil;
import com.cwx.mychat.websocket.message.SendMessageHandler;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 作者： cwx
 * 时间： 2024/9/21 14:33
 * 版本： 1.0
 * 注释：
 */
@Slf4j
@Service
public class ChatMessageServiceImpl extends ServiceImpl<ChatMessageMapper, ChatMessage> implements ChatMessageService {

    @Autowired
    private ChatMessageMapper chatMessageMapper;

    @Autowired
    private UserContactMapper userContactMapper;

    @Autowired
    private ChatSessionService chatSessionService;

    @Autowired
    private RedisComponent redisComponent;

    @Autowired
    private SendMessageHandler sendMessageHandler;

    @Autowired
    private MinioUtil minioUtil;


    @Override
    @Transactional
    public R sendMessage(ChatMessage chatMessage, TokenUserInfoDto tokenUserInfoDto) throws BusinessException {
        //处理消息,放在html注入
        chatMessage.setMessageContent(StringUtil.cleanHtmlTag(chatMessage.getMessageContent()));
        //查看消息类型
        Integer messageType = chatMessage.getMessageType();
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(messageType);
        if(messageTypeEnum == null ||
                !ArrayUtils.contains(new Integer[]{MessageTypeEnum.CHAT.getType(), MessageTypeEnum.MEDIA_CHAT.getType()}, messageType)){
            throw new BusinessException(ExceptionBizCodeEnum.MESSAGE_TYPE_ERROR);
        }
        //查询该用户是否被接收人拉黑或删除
        UserContact userContact = userContactMapper.selectOne(new LambdaQueryWrapper<UserContact>()
                        .nested(w -> w.eq(UserContact::getUserId, tokenUserInfoDto.getUserId())
                                .eq(UserContact::getContactId, chatMessage.getContactId()))
                        .or()
                        .nested(w -> w.eq(UserContact::getUserId, chatMessage.getContactId())
                                .eq(UserContact::getContactId, tokenUserInfoDto.getUserId()))
                        .select(UserContact::getStatus));
        if (userContact == null) {
            throw new BusinessException(ExceptionBizCodeEnum.NOT_FOUND_ACCOUNT);
        }
        //判断是群聊还是单聊
        UserContactTypeEnum prefix = UserContactTypeEnum.getPrefix(chatMessage.getContactId());
        if(prefix == null){
            throw new BusinessException(ExceptionBizCodeEnum.NOT_FOUND_ACCOUNT);
        }
        Integer userContactStatus = userContact.getStatus();
        if (userContactStatus.equals(UserContactStatusEnum.BLOCKED.getStatus())
                || userContactStatus.equals(UserContactStatusEnum.BE_BLOCKED.getStatus())
                || userContactStatus.equals(UserContactStatusEnum.DELETED.getStatus())
                || userContactStatus.equals(UserContactStatusEnum.BE_DELETED.getStatus())) {
            //无法发送消息，返回前端
            HashMap<String , Object> map = new HashMap<>();
            map.put("type", prefix.getType());
            map.put("status", userContactStatus);
            //TODO 删除chatSessionUser 和 ChatSession
            return R.error().put(CodeConstant.RESULT_MSG, map);
        }
        //保存信息
        int insert = this.saveMessage(chatMessage, tokenUserInfoDto);
        //更新session
        ChatSession chatSession = chatSessionService.updateSession(chatMessage, tokenUserInfoDto, prefix);
        if(chatSession == null){
            log.error("更新最终会话失败");
        }
        //将信息拷贝到Vo中
        MessageSendVo messageSendVo = new MessageSendVo();
        BeanUtils.copyProperties(chatMessage, messageSendVo);
        messageSendVo.setMessageId(chatMessage.getMessageId().toString());
        if (insert > 0 && chatSession != null) {
            //发送消息
            messageSendVo.setLastMessage(chatSession.getLastMessage());
            messageSendVo.setLastReceiveTime(chatSession.getLastReceiveTime());
            messageSendVo.setSendTime(chatMessage.getSendTime());
            sendMessageHandler.sendMessage(messageSendVo);
            return R.ok().put(CodeConstant.RESULT_MSG, messageSendVo);
        }else {
            log.error("发送失败， 未保存发送信息！");
            return R.error("消息发送失败");
        }
    }

    //保存信息
    @Override
    public int saveMessage(ChatMessage chatMessage, TokenUserInfoDto tokenUserInfoDto) {
        chatMessage.setSendUserId(tokenUserInfoDto.getUserId());
        chatMessage.setSendUserNickName(tokenUserInfoDto.getNickName());
        chatMessage.setCreateTime(new Date());
        chatMessage.setSendTime(new Date().getTime());
        chatMessage.setSessionId(CommonUtil.getChatSessionIdUser(new String[]{tokenUserInfoDto.getUserId(), chatMessage.getContactId()}));
        chatMessage.setStatus(MessageStatusEnum.SEND.getStatus());
        //对消息进行加密处理
        String messageContent = chatMessage.getMessageContent();
        if(!StringUtil.isEmpty(messageContent)){
            try {
                chatMessage.setMessageContent(AESUtils.encryptLargeData(messageContent));
            } catch (Exception e) {
                log.error("加密失败，采用明文储存", e);
            }
        }
        int insert = chatMessageMapper.insert(chatMessage);
        //恢复名文内容
        chatMessage.setMessageContent(messageContent);
        return insert;

    }


    @Override
    public R sendFile(MultipartFile file, TokenUserInfoDto tokenUserInfoDto, Long messageId) throws BusinessException {
        String userId = tokenUserInfoDto.getUserId();
        ChatMessage chatMessage = chatMessageMapper.selectById(messageId);
        if(chatMessage == null){
            throw new RuntimeException(ExceptionBizCodeEnum.MESSAGE_NOT_EXIST.getMsg());
        }
        if(!chatMessage.getSendUserId().equals(userId)){
            throw new RuntimeException(ExceptionBizCodeEnum.MESSAGE_NOT_MATCH_USER.getMsg());
        }
        SysSettingConstant sysSetting = redisComponent.getSysSetting();
        String fileSuffix = StringUtil.getFileSuffix(file.getOriginalFilename());
        if (StringUtil.isEmpty(fileSuffix)){
            log.error("上传文件格式错误， 后缀为空");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_UPLOAD_FAILED);
        }
        //获取文件类型
        String fileType = getFileType(fileSuffix);
        if(ArrayUtils.contains(FileSettingConstant.IMAGE_SUFFIX_LIST, fileSuffix.toLowerCase())
                && file.getSize() > sysSetting.getMaxImageSize() * FileSettingConstant.FILE_SIZE_MB){
            //文件格式不对，返回给前端
            log.error("图片文件太大");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_FORMAT_ERROR);
        }else if(ArrayUtils.contains(FileSettingConstant.VIDEO_SUFFIX_LIST, fileSuffix.toLowerCase())
                && file.getSize() > sysSetting.getMaxVideoSize() * FileSettingConstant.FILE_SIZE_MB){
            //视频格式不对， 返回给前端
            log.error("视频文件太大");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_FORMAT_ERROR);
        }else if(ArrayUtils.contains(FileSettingConstant.AUDIO_SUFFIX_LIST, fileSuffix.toLowerCase())
                && file.getSize() > sysSetting.getMaxAudioSize() * FileSettingConstant.FILE_SIZE_MB){
            //音频格式不对， 返回给前端
            log.error("音频文件太大");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_FORMAT_ERROR);
        } else if (file.getSize() > sysSetting.getMaxFileSize() * FileSettingConstant.FILE_SIZE_MB){
            //不是图视频文件,但文件太大
            log.error("其他文件太大");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_FORMAT_ERROR);
        }
        //保存到数据库和minio中
        String imageUrl = minioUtil.uploadFile(file, fileType);
        System.out.println(imageUrl);
        chatMessage.setStatus(MessageStatusEnum.SEND.getStatus());
        chatMessage.setFilePath(imageUrl);
        chatMessageMapper.updateById(chatMessage);

        MessageSendVo messageSendVo = new MessageSendVo();
        messageSendVo.setStatus(MessageStatusEnum.SEND.getStatus());
        messageSendVo.setMessageId(messageId.toString());
        messageSendVo.setSendUserId(userId);
        messageSendVo.setFilePath(chatMessage.getFilePath());
        messageSendVo.setFileName(chatMessage.getFileName());
        messageSendVo.setSendTime(chatMessage.getSendTime());
        messageSendVo.setFileType(chatMessage.getFileType());
        messageSendVo.setMessageType(MessageTypeEnum.FILE_UPLOAD.getType());
        messageSendVo.setContactId(chatMessage.getContactId());
        sendMessageHandler.sendMessage(messageSendVo);
        return R.ok();
    }

    private String getFileType(String fileSuffix) {
        if (ArrayUtils.contains(FileSettingConstant.IMAGE_SUFFIX_LIST, fileSuffix.toLowerCase())) {
            return FileSettingConstant.IMAGE_CATALOGUE;
        } else if (ArrayUtils.contains(FileSettingConstant.VIDEO_SUFFIX_LIST, fileSuffix.toLowerCase())) {
            return FileSettingConstant.VIDEO_CATALOGUE;
        } else if (ArrayUtils.contains(FileSettingConstant.AUDIO_SUFFIX_LIST, fileSuffix.toLowerCase())) {
            return FileSettingConstant.AUDIO_CATALOGUE;
        }
        return FileSettingConstant.FILE_CATALOGUE; // 默认类型
    }

    @Override
    public R saveMessageFile(TokenUserInfoDto tokenUserInfoDto, Long messageId, HttpServletResponse response) {
        ServletOutputStream outputStream = null;
        InputStream inputStream = null;
        try{
            //根据信息Id查询文件路径
            ChatMessage chatMessage = chatMessageMapper.selectById(messageId);
            if(chatMessage == null){
                throw new RuntimeException(ExceptionBizCodeEnum.ACCOUNT_FILE_NO_UPLOAD.getMsg());
            }
            String filePath = chatMessage.getFilePath();
            if(filePath == null){
                throw new RuntimeException(ExceptionBizCodeEnum.FILE_URL_EXIST.getMsg());
            }
            //获取文件名
            String urlFileName = StringUtil.getUrlFileName(filePath);
            inputStream = minioUtil.downloadFile(StringUtil.getUrlFileName(urlFileName));
            if(inputStream == null){
                throw new RuntimeException(ExceptionBizCodeEnum.MINIO_FILE_EXIST.getMsg());
            }
            response.setContentType("application/x-msdownload;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;");
            outputStream = response.getOutputStream();
            byte[] bytes = new byte[1024];
            int len;
            while ((len = inputStream.read(bytes)) != -1){
                outputStream.write(bytes, 0, len);
            }
            outputStream.flush();
        }catch (Exception e){
            log.error("文件下载失败", e);
            e.printStackTrace();
        }finally {
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("IO异常关闭失败", e);
                }
            }
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("IO异常关闭失败", e);
                }
            }
        }
        return R.ok();
    }

}
